package score.gui;


import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.HashMap;

import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.ListCellRenderer;
import javax.swing.border.Border;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import score.GrayScaleImage;
import score.IGrayScaleImage;
import score.IImage;
import score.IRGBImage;
import score.RGBImage;
import score.operators.MorphlogicalOperators;
import score.operators.OperatorsByIFT;
import score.utils.AdjacencyRelation;
import score.utils.ImageBuilder;

public class WindowWaltershed extends JFrame implements MouseListener, ActionListener, MouseMotionListener, ChangeListener {
	private final int PEN_OP = 1;

	/* Current mouse coordinates */
	private int mousex = 0;
	private int mousey = 0;

	/* Previous mouse coordinates */
	private int prevx = 0;
	private int prevy = 0;

	/* Initial state falgs for operation */
	private boolean initialPen = true;
	private int flagView = 1;

	
	private int udefAlphaValue = 25;
	
	/* Primitive status & color variables */
	private int opStatus = PEN_OP;
	private Color userDefinedColor = new Color(0, 255, 20);

	/* User defined Color variables */
	private JSlider alphaSlider = new JSlider(JSlider.HORIZONTAL, 0,	100, udefAlphaValue);
	Border borda = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.BLACK,3));
	
	private JButton applyButton = new JButton("Apply");
	private JButton reloadButton = new JButton("Reload");
	private JButton viewButton = new JButton("View");
	
	ComboBoxColor comboColor = new ComboBoxColor();
	/* Sub panels of the main applet */
	private JPanel controlPanel = new JPanel(new GridLayout(2, 0, 0, 0));
	private JPanel drawPanel = new JPanel();
	private JPanel udefcolPanel = new JPanel(new GridLayout(3, 0, 0, 0));
	private JPanel buttonPanel = new JPanel(new GridLayout(5, 0, 0, 0));
	 
	private BufferedImage imgInput;
	private BufferedImage imgCurrent;
	private IRGBImage imgLabel;
	int imgMarcador[][];
	AdjacencyRelation adj = AdjacencyRelation.getCircular(1.5f);
	
	public WindowWaltershed(IImage img) {
		this( (BufferedImage) img.createImage(), "Segmentacao iterativa com waltershed por marcadores");
	}
	
	
	public WindowWaltershed(BufferedImage img, String titulo) {
		super(titulo);
		this.imgInput = img;
		this.imgCurrent = new BufferedImage(imgInput.getWidth(), imgInput.getHeight(), BufferedImage.TYPE_INT_ARGB);
		
		
		controlPanel.add(udefcolPanel);
		controlPanel.add(buttonPanel);
		
		buttonPanel.add(reloadButton);
		buttonPanel.add(applyButton);
		buttonPanel.add(viewButton);
		
		reloadButton.addActionListener(this);
		viewButton.addActionListener(this);
		applyButton.addActionListener(this);
		
		alphaSlider.setBorder(BorderFactory.createTitledBorder("Alpha"));
		alphaSlider.setMajorTickSpacing(25);
		alphaSlider.setMinorTickSpacing(10);
		alphaSlider.setPaintTicks(true);
		alphaSlider.setPaintLabels(true);
		udefcolPanel.add(alphaSlider, BorderLayout.NORTH);
		
	    //Create and set up the content pane.
		//udefcolPanel.add(new JLabel("Label"));
        udefcolPanel.add(comboColor);
        comboColor.addActionListener(this);
        
		controlPanel.setBackground(Color.WHITE);
		drawPanel.setBackground(Color.WHITE);
		Cursor hourglassCursor = new Cursor(Cursor.CROSSHAIR_CURSOR);
		drawPanel.setCursor(hourglassCursor);
		super.setLayout(new BorderLayout());
		super.add(controlPanel, BorderLayout.WEST);
		super.add(drawPanel, BorderLayout.CENTER);

		alphaSlider.addChangeListener(this);
		
		drawPanel.addMouseMotionListener(this);
		drawPanel.addMouseListener(this);
		super.addMouseListener(this);
		super.addMouseMotionListener(this);
		super.setSize(img.getWidth() + 192, img.getHeight()+25);
		super.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		super.setVisible(true);	
		super.setResizable(false);
	
		reloadButton.doClick();
	}
	
	public void actionPerformed(ActionEvent e) {
		if(e.getSource() == comboColor){
			updateRGBValues();
		}else if(e.getSource() == reloadButton){
			updateRGBValues();
			updateImage();
		}else if(e.getSource() == viewButton){
			viewMarcador();
		}
		else if(e.getSource() == applyButton){
			apply();
		}
	}
	
	
	
	public void apply(){
		IGrayScaleImage imgGrad = MorphlogicalOperators.gradient(new GrayScaleImage(ImageBuilder.convertToArray(imgInput)), adj);
	
		HashMap<Integer, Integer> labels = new HashMap<Integer, Integer>();
		int label = 1;
		IGrayScaleImage imgM = new GrayScaleImage(imgInput.getWidth(), imgInput.getHeight());
		for(int x=0; x < imgInput.getWidth(); x++){
			for(int y=0; y < imgInput.getHeight(); y++){
				if(imgMarcador[x][y] != -1){
					if(labels.containsKey(imgMarcador[x][y])){
						imgM.setPixel(x, y, labels.get(imgMarcador[x][y]));
					}else{
						labels.put(imgMarcador[x][y], label++);
						imgM.setPixel(x, y, labels.get(imgMarcador[x][y]));
					}
				}else{
					imgM.setPixel(x,  y, -1);
				}
			}
		}
		
		IGrayScaleImage imgWS = OperatorsByIFT.watershedByMarker(adj, imgGrad, imgM);
		imgLabel = imgWS.randomColor();
		updateWS();
	}
	
	public void updateWS(){
		Graphics2D g = (Graphics2D) drawPanel.getGraphics();
		g.drawImage(imgInput, 1, 1, null);
		imgCurrent = new BufferedImage(imgInput.getWidth(), imgInput.getHeight(), BufferedImage.TYPE_INT_ARGB);
		double alpha = alphaSlider.getValue();
		alpha = 255 - (alphaSlider.getValue() / 100.0) * 255; 
		if(alpha > 255) alpha = 255;
		for(int x=0; x < imgInput.getWidth(); x++){
			for(int y=0; y < imgInput.getHeight(); y++){
				Color c = imgLabel.getPixel(x, y);
				imgCurrent.setRGB(x, y, new Color(c.getRed(),c.getGreen(), c.getBlue(),(int) alpha).getRGB());
			}
		}
		g.drawImage(imgCurrent, 1, 1, null);
	}
	
	public void viewMarcador(){
		imgCurrent = new BufferedImage(imgInput.getWidth(), imgInput.getHeight(), BufferedImage.TYPE_INT_ARGB);
		if(flagView == 1){
			for(int x=0; x < imgCurrent.getWidth(); x++){
				for(int y=0; y < imgCurrent.getHeight(); y++){
					if(imgMarcador[x][y] == -1)
						imgCurrent.setRGB(x, y, Color.BLACK.getRGB());
					else
						imgCurrent.setRGB(x, y, imgMarcador[x][y]);
				}
			}
			
		}else if(flagView == 0){
			for(int x=0; x < imgCurrent.getWidth(); x++){
				for(int y=0; y < imgCurrent.getHeight(); y++){
					if(imgMarcador[x][y] != -1){
						imgCurrent.setRGB(x, y, imgMarcador[x][y]);
					}else{
						imgCurrent.setRGB(x, y, imgInput.getRGB(x, y));
					}
				}
			}
			
		}
		else{
			IGrayScaleImage imgGrad = MorphlogicalOperators.gradient(new GrayScaleImage(ImageBuilder.convertToArray(imgInput)), adj);
			for(int x=0; x < imgCurrent.getWidth(); x++){
				for(int y=0; y < imgCurrent.getHeight(); y++){
					if(imgMarcador[x][y] != -1){
						imgCurrent.setRGB(x, y, imgMarcador[x][y]);
					}else{
						imgCurrent.setRGB(x, y, new Color(imgGrad.getPixel(x, y), imgGrad.getPixel(x, y), imgGrad.getPixel(x, y)).getRGB() );
					}
					
				}
			}
		}
		
		Graphics2D g = (Graphics2D) drawPanel.getGraphics();
		g.drawImage(imgCurrent, null, 1, 1);
		flagView += 1;
		if(flagView > 2)
			flagView = 0;
	}
	
	public void updateImage(){
		imgMarcador = new int[imgInput.getWidth()][imgInput.getHeight()];
		for(int x=0; x < imgInput.getWidth(); x++)
			for(int y=0; y < imgInput.getHeight(); y++)
				imgMarcador[x][y] = -1;
		
		Graphics2D g = (Graphics2D) drawPanel.getGraphics();
		g.drawImage(imgInput, 1, 1, null);
		imgCurrent = imgInput;
	}


	public void penOperation(MouseEvent e) {
		Graphics2D g = (Graphics2D) drawPanel.getGraphics();
		g.setColor(userDefinedColor);
		g.setStroke(new BasicStroke(4.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
		
		
		
		if (initialPen) {
			setGraphicalDefaults(e);
			initialPen = false;
			
			drawLine(prevx, prevy, mousex, mousey, userDefinedColor.getRGB());
			g.drawLine(prevx, prevy, mousex, mousey);
			
		}

		if (mousex != e.getX() || mousey != e.getY()) {
			mousex = e.getX();
			mousey = e.getY();
			
			drawLine(prevx, prevy, mousex, mousey, userDefinedColor.getRGB());
			g.drawLine(prevx, prevy, mousex, mousey);
			
			prevx = mousex;
			prevy = mousey;
		}
	}

	public void setGraphicalDefaults(MouseEvent e) {
		mousex = e.getX();
		mousey = e.getY();
		prevx = e.getX();
		prevy = e.getY();
		
	}

	public void mouseDragged(MouseEvent e) {
		switch (opStatus) {
		case PEN_OP:
				penOperation(e);
				break;

		}
	}
	
	

	public void mouseReleased(MouseEvent e) {
		switch (opStatus) {
		case PEN_OP:
			releasedPen();
			break;

		}
	}

	public void mouseEntered(MouseEvent e) {}
	public void mouseClicked(MouseEvent e) { 
		switch (opStatus) {
		case PEN_OP:
				penOperation(e);
				releasedPen();
				break;

		}
	}
	public void mouseExited(MouseEvent e) {}
	public void mouseMoved(MouseEvent e) {}
	public void mousePressed(MouseEvent e) {}

	public void releasedPen() {
		initialPen = true;
	}

	public void stateChanged(ChangeEvent e) {
		updateRGBValues();		
	}


	public void updateRGBValues() {
		userDefinedColor = comboColor.getColor();
		if(udefAlphaValue != alphaSlider.getValue()){
			udefAlphaValue = alphaSlider.getValue();
			if(imgLabel != null)
				updateWS();
		}
	}


	
	public void pintaPixel(int x, int y, int cor, int espessura){
		if(espessura == 1){
		
		if(x-2 >= 0 && x-2 < imgInput.getWidth() && y >= 0 && y < imgInput.getHeight() )
			imgMarcador[x-2][y] = cor;
		if(x-2 >= 0 && x-2 < imgInput.getWidth() && y-1 >= 0 && y-1 < imgInput.getHeight() )
			imgMarcador[x-2][y-1] = cor;
		if(x-2 >= 0 && x-2 < imgInput.getWidth() && y+1 >= 0 && y+1 < imgInput.getHeight() )
			imgMarcador[x-2][y+1] = cor;
		
		if(x-1 >= 0 && x-1 < imgInput.getWidth() && y-2 >= 0 && y-2 < imgInput.getHeight() )
			imgMarcador[x-1][y-2] = cor;
		if(x-1 >= 0 && x-1 < imgInput.getWidth() && y+2 >= 0 && y+2 < imgInput.getHeight() )
			imgMarcador[x-1][y+2] = cor;
		
		
		if(x >= 0 && x < imgInput.getWidth() && y-2 >= 0 && y-2 < imgInput.getHeight() )
			imgMarcador[x][y-2] = cor;
		if(x >= 0 && x < imgInput.getWidth() && y+2 >= 0 && y+2 < imgInput.getHeight() )
			imgMarcador[x][y+2] = cor;
		
		if(x+1 >= 0 && x+1 < imgInput.getWidth() && y-2 >= 0 && y-2 < imgInput.getHeight() )
			imgMarcador[x+1][y-2] = cor;
		if(x+1 >= 0 && x+1 < imgInput.getWidth() && y+2 >= 0 && y+2 < imgInput.getHeight() )
			imgMarcador[x+1][y+2] = cor;
		

		if(x+2 >= 0 && x+2 < imgInput.getWidth() && y >= 0 && y < imgInput.getHeight() )
			imgMarcador[x+2][y] = cor;
		if(x+2 >= 0 && x+2 < imgInput.getWidth() && y-1 >= 0 && y-1 < imgInput.getHeight() )
			imgMarcador[x+2][y-1] = cor;
		if(x+2 >= 0 && x+2 < imgInput.getWidth() && y+1 >= 0 && y+1 < imgInput.getHeight() )
			imgMarcador[x+2][y+1] = cor;
		
		}
		
		if(x-1 >= 0 && x-1 < imgInput.getWidth() && y >= 0 && y < imgInput.getHeight() )
			imgMarcador[x-1][y] = cor;
		if(x-1 >= 0 && x-1 < imgInput.getWidth() && y-1 >= 0 && y-1 < imgInput.getHeight() )
			imgMarcador[x-1][y-1] = cor;
		if(x-1 >= 0 && x-1 < imgInput.getWidth() && y+1 >= 0 && y+1 < imgInput.getHeight() )
			imgMarcador[x-1][y+1] = cor;
		
		if(x >= 0 && x < imgInput.getWidth() && y >= 0 && y < imgInput.getHeight() )
			imgMarcador[x][y] = cor;
		if(x >= 0 && x < imgInput.getWidth() && y-1 >= 0 && y-1 < imgInput.getHeight() )
			imgMarcador[x][y-1] = cor;
		if(x >= 0 && x < imgInput.getWidth() && y+1 >= 0 && y+1 < imgInput.getHeight() )
			imgMarcador[x][y+1] = cor;
		
		if(x+1 >= 0 && x+1 < imgInput.getWidth() && y >= 0 && y < imgInput.getHeight() )
			imgMarcador[x+1][y] = cor;
		if(x+1 >= 0 && x+1 < imgInput.getWidth() && y-1 >= 0 && y-1 < imgInput.getHeight() )
			imgMarcador[x+1][y-1] = cor;
		if(x+1 >= 0 && x+1 < imgInput.getWidth() && y+1 >= 0 && y+1 < imgInput.getHeight() )
			imgMarcador[x+1][y+1] = cor;
		
		
	}
	
	public  void drawLine(int x1, int y1, int x2, int y2, int cor){
		
		if(Math.abs( x2 - x1 ) > Math.abs( y2 - y1 )){
			if(x1 > x2) drawLine(x2, y2, x1, y1, cor);
			int a = x2 - x1;
			int b = y2 -y1;
			
			int inc = 1;
			if(b<0){
				inc = -1;
				b = -b;
			}
			int v = 2 * a + b;
			int neg = 2 * b;
			int pos = 2 * (b - a);
			int x = x1;
			int y = y1;
			pintaPixel(x, y, cor, 1);	
			while (x<= x2){
				 
				pintaPixel(x, y, cor, 1);	
				x= x + 1;
				if(v <= 0){
					v = v + neg;
				}else{
					y = y + inc;
					v = v+ pos;
				}
			}
		}else{
			if(y1 > y2) drawLine(x2, y2, x1, y1, cor);
			int b = x2 - x1;
			int a = y2 - y1;
			int inc = 1;
			if( b < 0){
				inc = -1;
				b = -b;
			}
			int v = 2 * b - a;
			int neg = 2 * b;
			int pos = 2 * (b - a);
			int x = x1;
			int y = y1;
			pintaPixel(x, y, cor, 1);	
			while(y <= y2){
				
				pintaPixel(x, y, cor, 1);
				y = y + 1;
				if(v <= 0){
					v = v + neg;
				}else{
					x = x + inc;
					v = v + pos;
				}
			}
		}
	}

	public static void main(String args[]) throws IOException {
		IRGBImage img = new RGBImage(ImageIO.read(ImageBuilder.windowOpenFile()));
		
		WindowWaltershed win = new WindowWaltershed(img);
		win.setVisible(true);
	}
}




 class ComboBoxColor extends JComboBox {
    ImageIcon[] images;
    public static Color[] colors = new Color[]{Color.GREEN, Color.ORANGE, Color.YELLOW, Color.PINK, Color.MAGENTA,  Color.RED, Color.BLUE, Color.LIGHT_GRAY};
    static Integer[] intArray = new Integer[]{0, 1, 2, 3, 4, 5, 6, 7};
   
    public BufferedImage paintImage(int i){
    	Color c = colors[i];
    	BufferedImage img = new BufferedImage(140, 35, BufferedImage.TYPE_INT_RGB);
    	for(int x=0; x < img.getWidth(); x++){
    		for(int y=0; y < img.getHeight(); y++){
    			img.setRGB(x, y, c.getRGB());
    		}
    	}
    	return img;
    }
   
    public ComboBoxColor() {
    	 super(intArray);
    	 images = new ImageIcon[colors.length];
         for (int i = 0; i < colors.length; i++) {
    		 images[i] = new ImageIcon(paintImage(i));
    	 }
        ComboBoxRenderer renderer = new ComboBoxRenderer();
        renderer.setPreferredSize(new Dimension(140, 35));
        super.setRenderer(renderer);
        super.setMaximumRowCount(7);
    }
    
   
    public Color getColor(){
    	return colors[super.getSelectedIndex()];
    }


    class ComboBoxRenderer extends JLabel implements ListCellRenderer {
       
    	
        public ComboBoxRenderer() {
            setOpaque(true);
            setHorizontalAlignment(CENTER);
            setVerticalAlignment(CENTER);
        }

        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
            int selectedIndex = ((Integer)value).intValue();
            if (isSelected) {
                setBackground(list.getSelectionBackground());
                setForeground(list.getSelectionForeground());
            } else {
                setBackground(list.getBackground());
                setForeground(list.getForeground());
            }
            setIcon(images[selectedIndex]);
            return this;
        }

       
    }
}